﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Algobox.JuicyLib.TimeSeries;

namespace Algobox.JuicyLib.Analysis
{
    public class OHLCGenerator
    {

        /// <summary>
        /// Defaults to seconds
        /// </summary>
        private IntervalTypes intervalType = IntervalTypes.SECONDS;
        protected IntervalTypes IntervalType
        {
            get { return intervalType; }
            set { intervalType = value; }
        }
        /// <summary>
        /// Type of interval: seconds or ticks
        /// </summary>
        public enum IntervalTypes
        {
            TICKS,
            SECONDS
        }

        private int intervalPeriod = 1;
        public int IntervalPeriod
        {
            get { return intervalPeriod; }
            set { intervalPeriod = value; }
        }

        /// <summary>
        /// Counts the number of tick transpired
        /// Required when the IntervalType is set to TICKS
        /// </summary>
        private int intervalCounter = 0;
        public int IntervalCount
        {
            get { return intervalCounter; }
            set { intervalCounter = value; }
        }
        
        private OHLCTimeSeries timeseries = new OHLCTimeSeries();

        /// <summary>
        /// Creates a OHLC timeseries for a given time or tick period
        /// </summary>
        /// <param name="intervalPeriod">Defaults to 1</param>
        public OHLCGenerator(int intervalPeriod, IntervalTypes intervalType)
        {
            IntervalType = intervalType;

            if (intervalPeriod > 0)
                IntervalPeriod = intervalPeriod;
        }



        public void AddTick(BaseTimeSeriesRecord record)
        {
            CheckForGaps();

            OHLCTimesSeriesRecord currentRecord = (OHLCTimesSeriesRecord)timeseries.GetLastTimeSeriesRecord();

            if (IntervalType == IntervalTypes.SECONDS)
            {
                if (currentRecord.IsGapFill)
                {
                    currentRecord.SetData(record.RecordValue);
                }

                DateTime currentEndPeriod = currentRecord.RecordTime.AddSeconds(IntervalPeriod);

                if (currentRecord.High < record.RecordValue)
                    currentRecord.High = record.RecordValue;

                if (currentRecord.Low > record.RecordValue)
                    currentRecord.Low = record.RecordValue;
                // update existing record
                currentRecord.Close = record.RecordValue;
            }
            else if (IntervalType == IntervalTypes.TICKS)
            {
                if (++IntervalCount < IntervalPeriod)
                {
                    if (currentRecord.High < record.RecordValue)
                        currentRecord.High = record.RecordValue;

                    if (currentRecord.Low > record.RecordValue)
                        currentRecord.Low = record.RecordValue;

                    currentRecord.Close = record.RecordValue;
                }
                else
                {
                    timeseries.AddTimeSeriesRecord(new OHLCTimesSeriesRecord(record.RecordValue));
                    IntervalCount = 0;
                }

            }
 
        }


        /// <summary>
        /// Returns the last complete records from the time series
        /// </summary>
        /// <param name="numElements">Number of records to get</param>
        /// <returns>A List of OHLC time series records</returns>
        public OHLCTimeSeries GetLastCompletedRecords(int numElements)
        {
            CheckForGaps();

            OHLCTimeSeries records = timeseries.GetLastCompleteRecords(numElements);
            if(records != null)
                records.TimeLast = records[numElements - 1].RecordTime;
            return records;
        }


        /// <summary>
        /// Returns the time of the last record
        /// </summary>
        /// <returns></returns>
        public DateTime GetLastRecordTime()
        {
            return timeseries.TimeLast;
        }


        /// <summary>
        /// Returns the last complete records from the time series
        /// </summary>
        /// <param name="numElements"></param>
        /// <returns></returns>
        public void CheckForGaps()
        {
            DateTime now = DateTime.Now;            

            OHLCTimesSeriesRecord currentRecord = (OHLCTimesSeriesRecord)timeseries.GetLastTimeSeriesRecord();

            if (currentRecord == null)
            {
                timeseries.TimeFirst = now;
                timeseries.TimeLast = now;
                AddGapFill(now);
                return;
            }

            now = now.AddSeconds(-IntervalPeriod);

            if (IntervalType == IntervalTypes.SECONDS)
            {
                DateTime currentRecordTime = currentRecord.RecordTime;

                // check and create gap fills if required
                while (currentRecordTime < now)
                {
                    currentRecordTime = currentRecordTime.AddSeconds(IntervalPeriod);
                    // add gap fill record
                    AddGapFill(currentRecordTime);                    
                }

                timeseries.TimeLast = currentRecordTime;
            }

            return;
        }



        /// <summary>
        /// Adds a gap fill record to the time series
        /// </summary>
        /// <param name="recordTime">Time stamp of the gap fill record</param>
        private void AddGapFill(DateTime recordTime)
        {            
            timeseries.AddTimeSeriesRecord(new OHLCTimesSeriesRecord(recordTime));
        }



    }
}
